import { getCardByType, } from "@/api/cards"
import { AnchorSpec, DragEventCallbackOptions, DragOptions, jsPlumbInstance } from "jsplumb"
import { Node, Edge, Card, Midpoint, Args } from "./models"
import { StatusManager } from "./statusmgr"
import { toMidpointUUID } from "./utils"


export const dumpFlowchart = (nodes: Node[], statusManager: StatusManager): { edges: Edge[], nodes: Node[] } => {
    const edges: Edge[] = JSON.parse(JSON.stringify(statusManager.edges))

    for (const i in edges) {
        edges[i].midpoints = []
    }
    for (let i = 0; i < statusManager.midpoints.length; i++) {
        const midpoint = statusManager.midpoints[i]
        const midpointPos = statusManager.getMidpointPos(toMidpointUUID(midpoint.edgeID, midpoint.index))
        midpoint.left = midpointPos.left
        midpoint.top = midpointPos.top
        const edge = edges.find((edge: Edge) => edge.id == midpoint.edgeID)
        if (edge == null) {
            console.error(edges, midpoint)
            throw "error"
        }

        edge.midpoints.push(midpoint)
    }
    for (let i = 0; i < edges.length; i++) {
        edges[i].midpoints.sort((a: Midpoint, b: Midpoint) => a.index - b.index)
    }
    return { edges, nodes }
}


export const addEndpointsToNode = async (plumbInstance: jsPlumbInstance,
    statusMgr: StatusManager,
    nodeElID: string, type: string, flip: boolean) => {
    const card: Card = await getCardByType(type)
    const inputs: string[] = (() => {
        if (card.inputVariable) {
            const ports = statusMgr.getNode(nodeElID).inputPorts
            if (ports == null) {
                throw Error("Input ports was variable, but the input ports are null.")
            }
            return ports
        } else {
            const l = []
            for (const i in card.inputs) {
                l.push(card.inputs[i].name)
            }
            return l
        }

    })()
    const outputs: string[] = (() => {
        if (card.outputVariable) {
            const ports = statusMgr.getNode(nodeElID).outputPorts
            if (ports == null) {
                throw Error("Output ports was variable, but the output ports are null.")
            }
            return ports
        } else {
            const l = []
            for (const i in card.outputs) {
                l.push(card.outputs[i].name)
            }
            return l
        }
    })()

    const inputNum = inputs.length
    const outputNum = outputs.length
    for (let i = 0; i < inputNum; i++) {
        const y = (1 / (inputNum + 1)) * (i + 1);
        const inputPortUUID = nodeElID + "-input-" + inputs[i]
        const anchor: AnchorSpec = flip ? [1, y, 1, 0, 0, 0] : [0, y, -1, 0, 0, 0]
        plumbInstance!.addEndpoint(nodeElID, {
            uuid: inputPortUUID,
            anchor: anchor, // [x, y, dx-rel, dy-rel, offsetx-px, offsety-px]
            isSource: false,
            isTarget: true,
            connectionsDetachable: false,
            maxConnections: 1,
            endpoint: ["Rectangle", { width: 8, height: 8 }],
            overlays: [
                ["Label", { label: inputs[i], id: "label", location: [0, -1], cssClass: "port-label" }]]
        });
    }
    for (let i = 0; i < outputNum; i++) {
        const outputPortUUID = nodeElID + "-output-" + outputs[i]
        const y = (1 / (outputNum + 1)) * (i + 1);
        const anchor: AnchorSpec = flip ? [0, y, -1, 0, 0, 0] : [1, y, 1, 0, 0, 0]
        plumbInstance!.addEndpoint(nodeElID, {
            uuid: outputPortUUID,
            anchor: anchor, // [x, y, dx-rel, dy-rel, offsetx-px, offsety-px]
            isSource: true,
            isTarget: false,
            connectionsDetachable: false,
            maxConnections: -1,
            endpoint: ["Rectangle", { width: 8, height: 8 }],
            overlays: [
                ["Label", { label: outputs[i], id: "label", location: [0, -1], cssClass: "port-label" }]],
            cssClass: ""
        });
    }
}

export const addNode = async (
    plumbInstance: jsPlumbInstance,
    statusMgr: StatusManager,
    nodeElID: string,
    type: string,
    dragCallback: (params: DragEventCallbackOptions) => void,
    afterDragCallback: (params: DragEventCallbackOptions) => void,
    flip: boolean
) => {
    plumbInstance.draggable(nodeElID, { drag: dragCallback, stop: afterDragCallback, grid: [5, 5] } as DragOptions);
    addEndpointsToNode(plumbInstance, statusMgr, nodeElID, type, flip)
}

export const addEdge = (edge: Edge, plumb: jsPlumbInstance): void => {
    return
}

export const addMidpoint = (plumb: jsPlumbInstance, statusMgr: StatusManager, midpointUUID: string): void => {
    plumb.addEndpoint(midpointUUID, {
        uuid: midpointUUID + "-input",
        isSource: false,
        isTarget: true,
        endpoint: ["Rectangle", { width: 1, height: 1 }],
        connectionsDetachable: false,
        anchor: [
            'Top', "Right", "Bottom", "Left"
        ],
        maxConnections: 1
    });
    plumb.addEndpoint(midpointUUID, {
        uuid: midpointUUID + "-output",
        isSource: true,
        isTarget: false,
        connectionsDetachable: false,
        endpoint: ["Rectangle", { width: 1, height: 1 }],
        anchor: [
            'Top', "Right", "Bottom", "Left"
        ],
        maxConnections: 1
    });


    plumb.draggable(midpointUUID, {
        start: () => {
            statusMgr.startAutoUpdate();
        },
        drag: (args: DragEventCallbackOptions) => {
            const draggedMidpointPos =
                statusMgr.getMidpointPos(midpointUUID);

            const newMidpointPositions =
                statusMgr.calculateMidpointsPositionWithConstraint(
                    midpointUUID,
                    draggedMidpointPos.left,
                    draggedMidpointPos.top
                );
            for (let i = 0; i < newMidpointPositions.length; i++) {
                const midpointPos = newMidpointPositions[i];
                statusMgr.setMidpointPosLazy(
                    midpointPos.midpointUUID,
                    midpointPos.left,
                    midpointPos.top
                );
            }
            // statusMgr.revalidate();
        },
        stop: () => {
            const midpoint: Midpoint = statusMgr.midpoints.find(
                (midpoint: Midpoint) =>
                    "midpoint-" + midpoint.edgeID + "-" + midpoint.index ==
                    midpointUUID
            ) as Midpoint;
            const elem: HTMLDivElement = document.getElementById(
                midpointUUID
            ) as HTMLDivElement;
            midpoint.left = parseInt(elem.style.left.split("px")[0]);
            midpoint.top = parseInt(elem.style.top.split("px")[0]);
            statusMgr.revalidate()
            statusMgr.stopAutoUpdate();

        },
    });

}

export function exportFile(name: string, data: string) {
    const urlObject = window.URL || window.webkitURL || window;
    const export_blob = new Blob([data]);
    const save_link: HTMLAnchorElement = document.createElementNS(
        "http://www.w3.org/1999/xhtml",
        "a"
    ) as HTMLAnchorElement;
    save_link.href = urlObject.createObjectURL(export_blob);
    save_link.download = name;
    fakeClick(save_link);
}
function fakeClick(obj: any) {
    const ev: any = document.createEvent("MouseEvents");
    ev.initMouseEvent(
        "click",
        true,
        false,
        window,
        0,
        0,
        0,
        0,
        0,
        false,
        false,
        false,
        false,
        0,
        null
    );
    obj.dispatchEvent(ev);
}


